public class ContactEntitlementListControllerAsset{

    private final Contact cont;
    // -- Local variables
    //List collection to store support program associated with Contact. 
    //The support programs will contain inside Contact row as object...so, the list object should be of Contact type.   
    List<Contact> spList = new List<Contact>();
    
    //List collection to store support program which is the lookup SP for an Account (Default SP)
    List<Support_Program__c> acSpList = new List<Support_Program__c>();
    
    //List collection to store all support programs associated with parent Account of this contact.
    List<Support_Program__c> otherSpList = new List<Support_Program__c>();

    //Contact.Id
    Id contId;

    //Contact's name
    String contName;

    //Account.Id, the parent of this contact.
    Id acId;

    //SPId of the parent account of this contact
    Id spIdOnAc;
    
    //Stores supportprogram ids that are retrieved from spList (directly related to this contact).
    //The items in this list will not be added in the allSupportProgramIds. This way we don't have duplicate SPs under dropdown
    Set<Id> contactSupportProgramIds = new Set<Id>();

    //Local variable to store all SPs that are selectible in Support Program dropdown.
    //This is useful to draw entitlement lists when user selects 'All' in the dropdown.
    List<Id> allSupportProgramIds = new List<Id>();
    
    //Local variable to store all SPs with Name and Id (Name with 80 spaces+'|'+Id)
    List<String> allSPNameAndIDs = new List<String>();
    
    //Local variable that holds currently selected program option in dropdown.
    //At first, it will be null and will hve value only when user selects an option from dropdown
    String programs;
    
    //Local variable to store the selected SP. The entitlement list displayed on the basis of this selected SP.
    String selectedSP = 'All';      

    //Constructor
    public ContactEntitlementListControllerAsset(ApexPages.StandardController stdController){
        this.cont = (Contact)stdController.getRecord();     
        //Store contact's id
        contId = System.CurrentPageReference().getParameters().get('id');
        //Find and store this contact's parent account's id 
        List<Contact> lstCont = new List<Contact>([SELECT Contact.Name, Contact.Account.Id FROM Contact where id=:contId]);
        this.contName = lstCont[0].Name;
        //Store parent account's id
        acId = lstCont[0].Account.Id;    
        //Get lookup SPId of the parent account of this Contact
        Account[] allAcs = [Select Support_Program__c from Account where Id=:acId];
        if(allAcs.size()>0){
            spIdOnAc = allAcs[0].Support_Program__c;
        }
        //Load all support programs
        loadSupportPrograms();
        getItems();
    }      
    
    //Dummy constructor for testMethod
    public ContactEntitlementListControllerAsset(Contact dc){
        this.cont = dc;
        contId = dc.Id;
        this.contName = dc.Name;
        acId = dc.Account.Id;
        System.debug('dummy constructor');      
        loadSupportPrograms();
        getItems();
    }
    
    //Loads support programs associated to both Contact and Account
    private void loadSupportPrograms(){
        //Support Programs list associated to this Contact
        spList = [Select Id, (Select Support_Program__c, Support_Program__r.Name From c.Program_Contacts__r Order By Support_Program__r.Name) From Contact c Where c.Id =:contId ];
        
        //Support Programs that are associated with parent account of this contact IF 'Apply_To_All_Contacts_In_Account__c' is true.
        otherSpList = [Select Id, Name From Support_Program__c where Account__c=:acId and Apply_To_All_Contacts_In_Account__c=True Order By Name];      
        
        //Support Programs that is the lookup SP of the parent account of this contact IF 'Apply_To_All_Contacts_In_Account__c' is true.
        acSpList= [Select Id, Name From Support_Program__c where Id=:spIdOnAc and Apply_To_All_Contacts_In_Account__c=True Order By Name];              
        
        //spList returns only one row containing an object of Support_Program__c.
        //So we need to iterate through this object to get all support programs.
        for(Contact program : spList){
            for(Program_Contact__c pc : program.Program_Contacts__r){
                //Add this id into contactSupportProgramIds collection so that SPs in this collection do not repeat while storing in allSPNameAndIDs
                contactSupportProgramIds.add(pc.Support_Program__c);
                //Add this id into allSPNameAndIDs
                if(EntitlementListAsset.HasEntForSP(pc.Support_Program__c)){
                    allSPNameAndIDs.add(EntitlementListAsset.CombineSPNameAndID(pc.Support_Program__r.Name,pc.Support_Program__c));
                }
            }
        }

        //--Add from otherSpList into allSPNameAndIDs. (Make sure to avoid duplicate)
        //otherSpList will not contain any object within it (like in case of spList), so simple iteration will be enough.
        for(Support_Program__c allSps : otherSpList){ 
            addToAllSPNamesAndIdsIffNew( contactSupportProgramIds, allSps.Id, allSps.Name);
        }
        
        //The acSpList contains just one record. So check if this sp is already included in contactSupportProgramIds and otherSpList. If so, remove the single row from acSpList.
        //This way, it can be declared that if the acSpList contains any row, then that has come from Account lookup, and not from other way, and must follow the rule while getting entitlement for this sp.
        system.assert((acSpList.size()==0) || (acSpList.size()==1));
        if(acSpList.size()>0) {
            //Boolean moreLoop = True;
            for(Id spId : contactSupportProgramIds){
                //if(moreLoop){
                    if(spId == acSpList[0].Id){
                        acSpList.remove(0);
                        //moreLoop = False;
                        break;
                    }
                //}
            }
        }

        if(acSpList.size()>0) {
            //Boolean moreLoop = True;
            for(Support_Program__c allSPs : otherSpList) {
                //if(moreLoop){
                    if(allSPs.Id == acSpList[0].Id){
                        acSpList.remove(0);
                        //moreLoop = False;
                        break;
                    }
                //}
            }
        }
        
        //If acSpList still has row, it means it was not repeated in contactSupportProgramIds and otherSpList and it is lookup SP for the account
        //--Add from acSpList into allSPNameAndIDs.
        if(acSpList.size()>0) {
            if(EntitlementListAsset.HasEntForSP(acSpList[0].Id)){
                allSPNameAndIDs.add(EntitlementListAsset.CombineSPNameAndID(acSpList[0].Name,acSpList[0].Id));
            }
        }       
        
        allSupportProgramIds = populateAllSPList(allSPNameAndIDs);

    }
    
    void addToAllSPNamesAndIdsIffNew(Set<Id> listToCheck, Id i, string n) { 
        Boolean matchFound = False;                
        for(Id ids : listToCheck) {
            if(ids == i){
                matchFound = True;
                system.debug('>>> addToAllSPNamesAndIdsIffNew():  REJECTING duplicate SP name|id concat "'+ n + '|' + i + '"');
                break;
            }                                
        }
        if(matchFound==False){
            //Add this id into allSPNameAndIDs
            if(EntitlementListAsset.HasEntForSP(i)){
                system.debug('>>> addToAllSPNamesAndIdsIffNew():  ADDING non-duplicate SP name|id concat "'+ n + '|' + i + '"');
                allSPNameAndIDs.add(EntitlementListAsset.CombineSPNameAndID(n,i));
            } else {
                system.debug('>>> addToAllSPNamesAndIdsIffNew():  REJECTING b/c !HasEntForSP( "'+ n + '|' + i + '") ');
            }
        }   
    }
    
    public static testMethod void testaddToAllSPNamesAndIdsIffNew() {
        Account acct = new Account(Name='acct'); insert acct;
        Contact c = new Contact(Account = acct, LastName='contactname'); insert c;
        ContactEntitlementListControllerAsset cont = new ContactEntitlementListControllerAsset(c);
        
        Set<Id> lstId = new Set<Id>();
        Account a = new Account(Name='accttest2'); insert a;
        Account b = new Account(Name='accttest3'); insert b;
        cont.addToAllSPNamesAndIdsIffNew( lstId, a.Id, 'abc' );
        
        lstId.add(a.Id);
        lstId.add(b.Id);
        cont.addToAllSPNamesAndIdsIffNew( lstId, b.Id, 'abc' );
        cont.addToAllSPNamesAndIdsIffNew( lstId, b.Id, 'abc' );
    }
    
    List<Id> populateAllSPList(List<String> allSPNameId) {
        List<Id> ret = new List<Id>();
        
        //Sort this list.
        allSPNameId.sort();
        for(String s : allSPNameId) {
            //Get id from this list and add into allSupportProgramIds
            ret.add( EntitlementListAsset.GetIDFromCombineSP(s) );                
        }
        
        return ret;
    }
    
    static testMethod void TestLoadPopulateAllSPList(){
        //Contact c = setupForLoadSPTestRun(true,true,true);
        UT_SObjectFactory.InterconnectedSObjectsForContact o = new UT_SObjectFactory.InterconnectedSObjectsForContact();
        Contact c = o.c;
        ContactEntitlementListControllerAsset ext = new ContactEntitlementListControllerAsset(c);
        ext.loadSupportPrograms();
        List<String> lstNameId = new string[]{'Abc|01p3000000007ec','Zyx|01p3000000007ec'};
        /*List<Id> lst = */ext.populateAllSPList(lstNameId);
    }

    static void runLoadSPForTest(Contact c) {
        ContactEntitlementListControllerAsset ext = new ContactEntitlementListControllerAsset(c);
        ext.loadSupportPrograms();
    }
    
    public String getContactName(){     
        return this.contName;
    }

    //Required by SelectOption
    //Returns list of support programs to be populated in 'Support Program' dropdown
    public List<SelectOption> getItems(){
        List<SelectOption> options = new List<SelectOption>();
        options.add(new SelectOption('All','All'));
        
        //Sort again just in case (though it is already sorted)
        allSPNameAndIDs.sort();
        for(String s : allSPNameAndIDs){
            //Add items into options for the dropdoen
            options.add(new SelectOption(EntitlementListAsset.GetIDFromCombineSP(s), EntitlementListAsset.GetNameFromCombineSP(s)));
        }
        return options;
    }
    
    //Getter/Setter for programs variable. Holds currently selected option in dropdown
    public String getPrograms(){
        return programs;
    }
    public void setPrograms(String programs){
        this.programs = programs;
    }
    
    //Returns if there is any entitlement to display for the selected SP from the dropdown.
    //This is the basis of displaying 'No Entitlements found for this...' label as well as displaying apex:pageBlockTable
    public Boolean getNoEntitlement(){
        List<EntitlementListAsset.EntitlementWrapper> el = getEntitlement();
        return el.size()==0;        
    }
    
    //Main method that displays Entitlement List for selected support program.
    //This method calls another apex method 'getEntitlementList' in EntitlementListAsset class with list of support programs' id as a parameter
    public List<EntitlementListAsset.EntitlementWrapper> getEntitlement() {
        
        EntitlementListAsset el = new EntitlementListAsset();
        List<EntitlementListAsset.EntitlementWrapper> entData;
        Boolean bDoCartesianProduct =True;
        //If programs is null or all, send all SPIDs as parameter so that all entitlements can be displayed
        if(programs==null || programs=='All'){
            
            entData = el.getEntitlementList(allSupportProgramIds, acSpList, bDoCartesianProduct );
        }       
        //otherwise, send single SPID as parameter which is currently being selected in the dropdown
        else{
            
            entData = el.getEntitlementList(new List<Id>{programs}, acSpList, bDoCartesianProduct );
        }

        return entData;
    }   
    
    //Returns new pagereference of new case window
    public PageReference CreateNewCase(){
        try{
            String prmFunc = System.currentPageReference().getParameters().get('prmFunc');
            ID prmEntId = System.currentPageReference().getParameters().get('prmEntId');
            ID prmSpId = System.currentPageReference().getParameters().get('prmSPId');
            String prmEntName = System.currentPageReference().getParameters().get('prmEntName');
            ID prmAstId = System.currentPageReference().getParameters().get('prmAstId');
          //  system.debug((string)prmAstId);
            String prmAstName = System.currentPageReference().getParameters().get('prmAstName');
            
            EntitlementListAsset el = new EntitlementListAsset();
            Id retUrlId = contId;
            return el.CreateNewCase(retUrlId, contId, acId, prmFunc, prmEntId, prmspId, prmEntName, prmAstId, prmAstName);  
        }
        catch (Exception ex){
            return null;
        }
    }    
    
}